//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-03-01     ZhangYihua   first version
//
// Description  : 
//################################################################################

module data_collect #(
parameter           DEPTH                   = 8192,
parameter           DATA_BW                 = 32,
parameter           MEM_RL                  = 2,

// the following parameters are calculated automatically
parameter           ADDR_BW                 = $clog2(DEPTH),
parameter  [ADDR_BW-1:0] DLY_LESS           = DEPTH/8-1,
parameter  [ADDR_BW-1:0] DLY_HALF           = DEPTH*4/8-1,
parameter  [ADDR_BW-1:0] DLY_MASS           = DEPTH*7/8-1
) ( 
input                                       rst_n,
input                                       clk,

input               [2-1:0]                 iaa_b2b,        // 0:single RW; 1: fast RW back-to-back; 2,3:reserved
input                                       iaa_wr,         // 1'b1: wtite; 1'b0: read
input                                       iaa_start,      // 1 clock pulse to start a single RW
input               [ADDR_BW-1:0]           iaa_addr,
output                                      iaa_addr_bwe,
output              [ADDR_BW-1:0]           iaa_addr_bwd,
input               [DATA_BW-1:0]           iaa_data,       // write data CPU -> mem
input                                       iaa_data_fwe,
input                                       iaa_data_fre,
output                                      iaa_data_bwe,
output              [DATA_BW-1:0]           iaa_data_bwd,   // read data CPU <- mem
input                                       iaa_ctrl_fwe,   // forward write enable for CTRL(b2b/wr/start/addr) reg
input                                       iaa_data_frw,   // forward read and write for DATA reg
output                                      iaa_miss,       // mem_ack must fast enough avoid to miss next RW
output                                      iaa_busy,

input                                       dc_vld,
input               [DATA_BW-1:0]           dc_data,
input                                       dc_event,

input                                       dc_start,
output                                      dc_start_clr,
input               [2-1:0]                 dc_mode,        // 0: stop at full; 1,2,3: stop after event for 1/8,4/8,7/8 DEPTH
output  reg         [ADDR_BW-1:0]           last_waddr,
output  reg                                 overlap_flag
);

//################################################################################
// define local varialbe and localparam
//################################################################################
localparam [ADDR_BW-1:0] ADDR_MAX           = DEPTH-1;

wire                                        cpu_req;
wire                                        cpu_wr;
wire                [ADDR_BW-1:0]           cpu_addr;
wire                [DATA_BW-1:0]           cpu_wdat;
wire                [DATA_BW-1:0]           cpu_rdat;
wire                                        cpu_ack;

wire                                        mem_wen;
wire                                        mem_ren;
wire                [ADDR_BW-1:0]           mem_addr;
wire                [DATA_BW-1:0]           mem_wdat;
wire                [DATA_BW-1:0]           mem_rdat;
reg                                         dc_start_1d;
wire                                        dc_start_rise;
reg                 [ADDR_BW-1:0]           dc_waddr;
wire                                        dc_waddr_over;
reg                                         event_dly_vld;
reg                 [ADDR_BW-1:0]           event_dly_cnt;
wire                                        event_dly_done;

//################################################################################
// main
//################################################################################

iaa2mem #(      // indirect address access(read/write) memory
        .DEPTH                          (DEPTH                          ),
        .DATA_BW                        (DATA_BW                        ),
        .INC_BW                         (1                              ),	// address increment bit width
        .MEM_RL                         (MEM_RL                         )	// meory read latency
) u_iaa2mem ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .iaa_b2b                        (iaa_b2b[0]                     ),	// 0:single RW; 1: fast RW back-to-back; others: reserved
        .iaa_wr                         (iaa_wr                         ),	// 1'b1: wtite; 1'b0: read
        .iaa_start                      (iaa_start                      ),	// 1 clock pulse to start a single RW
        .iaa_addr                       (iaa_addr                       ),
        .iaa_addr_bwe                   (iaa_addr_bwe                   ),
        .iaa_addr_bwd                   (iaa_addr_bwd                   ),
        .iaa_data                       (iaa_data                       ),	// write data CPU -> mem
        .iaa_data_fwe                   (iaa_data_fwe                   ),
        .iaa_data_fre                   (iaa_data_fre                   ),
        .iaa_data_bwe                   (iaa_data_bwe                   ),
        .iaa_data_bwd                   (iaa_data_bwd                   ),	// read data CPU <- mem
        .iaa_addr_inc                   (1'd1                           ),	// address increment at back-to-back mode
        .iaa_ctrl_fwe                   (iaa_ctrl_fwe                   ),	// forward write enable for CTRL(b2b/wr/start/addr) reg
        .iaa_data_frw                   (iaa_data_frw                   ),	// forward read and write for DATA reg
        .iaa_miss                       (iaa_miss                       ),	// mem_ack must fast enough avoid to miss next RW
        .iaa_busy                       (iaa_busy                       ),

        .mem_req                        (cpu_req                        ),
        .mem_wr                         (cpu_wr                         ),
        .mem_addr                       (cpu_addr                       ),
        .mem_wdat                       (cpu_wdat                       ),
        .mem_rdat                       (cpu_rdat                       ),	// rdat is valid after MEM_RL from mem_ack for read
        .mem_ack                        (cpu_ack                        )
);

assign cpu_ack = cpu_req;

spram_wrap #(
        .DEPTH                          (DEPTH                          ),
        .DATA_BW                        (DATA_BW                        ),
        .RDPL_NUM                       (MEM_RL                         ),	// Read Data Pipe Line number
        .USER_DEF_TAG                   ("NONE"                         )
) u_spram ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .mem_wen                        (mem_wen                        ),
        .mem_ren                        (mem_ren                        ),
        .mem_rce                        ({MEM_RL{1'b1}}                 ),
        .mem_addr                       (mem_addr                       ),
        .mem_wdat                       (mem_wdat                       ),
        .mem_rvld                       (                               ),
        .mem_rdat                       (mem_rdat                       ),
        .cfg_frc_sbe                    (1'b0                           ),	// support at ECC_MODE
        .alm_ecc_err                    (                               ),	// support at ECC_MODE
        .alm_ecc_dbe                    (                               )	// support at ECC_MODE
);

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        dc_start_1d <=`U_DLY 1'b0;
    end else if (dc_vld==1'b1) begin
        dc_start_1d <=`U_DLY dc_start;
    end else
        ;
end
assign dc_start_rise = (~dc_start_1d) & dc_start & dc_vld;

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        dc_waddr <=`U_DLY {ADDR_BW{1'b0}};
    end else begin
        if (dc_start==1'b0)
            dc_waddr <=`U_DLY {ADDR_BW{1'b0}};
        else if (dc_vld==1'b1)
            if (dc_waddr==ADDR_MAX)
                dc_waddr <=`U_DLY {ADDR_BW{1'b0}};
            else
                dc_waddr <=`U_DLY dc_waddr + 1'd1;
        else
            ;
    end
end

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        overlap_flag <=`U_DLY 1'b0;
    end else begin
        if (dc_start_rise==1'b1)
            overlap_flag <=`U_DLY 1'b0;
        else if ((dc_start==1'b1) && (dc_vld==1'b1) && (dc_waddr=={ADDR_BW{1'b0}}))
            overlap_flag <=`U_DLY 1'b1;
        else
            ;
    end
end

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        last_waddr <=`U_DLY {ADDR_BW{1'b0}};
    end else begin
        if ((dc_start==1'b1) && (dc_vld==1'b1))
            last_waddr <=`U_DLY dc_waddr;
        else
            ;
    end
end

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        event_dly_vld <=`U_DLY 1'b0;
    end else begin
        if (dc_start==1'b0)
            event_dly_vld <=`U_DLY 1'b0;
        else if (dc_event==1'b1 && event_dly_vld==1'b0)
            event_dly_vld <=`U_DLY 1'b1;
        else if (event_dly_done==1'b1 && event_dly_vld==1'b1)
            event_dly_vld <=`U_DLY 1'b0;
        else
            ;
    end
end

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        event_dly_cnt <=`U_DLY {ADDR_BW{1'b1}};
    end else begin
        if (dc_start_rise==1'b1) begin
            event_dly_cnt <=`U_DLY (dc_mode==2'd1) ? DLY_LESS :
                                   (dc_mode==2'd2) ? DLY_HALF :
                                                     DLY_MASS;
        end else if (dc_vld==1'b1 && event_dly_vld==1'b1)
            event_dly_cnt <=`U_DLY event_dly_cnt - 1'd1;
        else
            ;
    end
end
assign event_dly_done = (event_dly_cnt=={ADDR_BW{1'b0}}) ? dc_vld : 1'b0;

assign dc_waddr_over = (dc_waddr==ADDR_MAX) ? dc_vld : 1'b0;
assign dc_start_clr = (dc_mode==2'd0) ? dc_waddr_over : event_dly_done;

assign mem_wen  = (dc_start==1'b1) ? dc_vld   :  ( cpu_wr  & cpu_ack);
assign mem_ren  = (dc_start==1'b1) ? 1'b0     : ((~cpu_wr) & cpu_ack);
assign mem_addr = (dc_start==1'b1) ? dc_waddr : cpu_addr;
assign mem_wdat = (dc_start==1'b1) ? dc_data  : cpu_wdat;

assign cpu_rdat = mem_rdat;

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule
